import { Injectable, Logger } from '@nestjs/common';
import * as Minio from 'minio';
import { BusinessException } from '~/common/exceptions/biz.exception';
import * as _ from 'lodash';

@Injectable()
export class MinioService {
  private readonly _logger = new Logger('Minio服务');
  private _bucketName: string;
  private _minioClient: Minio.Client;
  private readonly _config = {
    endPoint: process.env.MINIO_ADDRESS || '127.0.0.1',
    useSSL: process.env.MINIO_USESSL === 'false' ? false : true,
    port: Number(process.env.MINIO_PORT) || 9000,
    accessKey: process.env.MINIO_ACCESSKEY || '',
    secretKey: process.env.MINIO_SECRETKEY || '',
    bucketName: process.env.MINIO_BUCKET_NAME || '',
  };

  constructor() {
    this._bucketName = process.env.MINIO_BUCKET_NAME;
    this._getInstance();
  }
  /**
   * 获取全局单实例
   */
  private _getInstance() {
    if (!this._minioClient) {
      this._minioClient = new Minio.Client(this._config);
    }
    return this._minioClient;
  }

  async saveFile(
    file: Express.Multer.File,
    userId: number
  ): Promise<{ msg: string; fileUrl: string }> {
    const fileName = file.originalname;
    // 一般文件编码只要是UTF-8 的就直接可以上传
    const fileBuffer = await file.buffer;
    try {
      // 调用minio的保存方法
      await this._minioClient.putObject(this._bucketName, fileName, fileBuffer);
    } catch (err) {
      throw new BusinessException('文件上传失败');
    }

    let fileUrl;

    if (process.env.NOWENV === 'prod') {
      fileUrl = process.env.MINIO_EXPOSE_IP + '/' + fileName;
    } else {
      fileUrl =
        'http://' +
        process.env.MINIO_ADDRESS +
        ':' +
        process.env.MINIO_PORT +
        '/' +
        process.env.MINIO_BUCKET_NAME +
        '/' +
        fileName;
    }
    return fileUrl;
  }

  // 根据路径删除对应的文件
  async deleteFile(objectName: string) {
    try {
      const { result } = await this.getDirectoryStructure(this._bucketName);
      if (!result.includes(objectName)) {
        throw new BusinessException('文件不存在');
      }
      await this._minioClient.removeObject(this._bucketName, objectName);
      return { msg: '文件删除成功' };
    } catch (err) {
      throw new BusinessException('文件删除失败');
    }
  }
  /**
   * 获取目录列表
   * @param bucketName 分组名
   * @returns director list
   */
  async getDirectoryStructure(bucketName: string) {
    if (_.isEmpty(bucketName)) {
      bucketName = this._bucketName;
    }
    const dataList = await this._listObjects(bucketName);
    const result: string[] = dataList.map(item => {
      const path = item.name;
      return path;
    });

    return { result };
  }

  private async _listObjects(bucketName: string): Promise<Minio.BucketItem[]> {
    const data = await new Promise<Minio.BucketItem[]>((resolve, reject) => {
      const objects: Minio.BucketItem[] = [];
      const stream = this._minioClient.listObjects(bucketName, '', true);

      stream.on('data', obj => {
        objects.push(obj as Minio.BucketItem);
      });
      stream.on('error', err => {
        reject(err);
      });
      stream.on('end', () => {
        resolve(objects);
      });
    });
    return data;
  }
}
